    .extern start_kernel

    .section .text.init         
    .globl _start
_start:
    la sp, boot_stack_top # 设置程序栈栈底sp
    li t0, 0xFFFFFFDF80000000
    sub sp, sp, t0

    call setup_vm
    call relocate

    call mm_init

    call setup_vm_final

    call task_init
    
	# 先将 trap 处理入口函数即_traps的入口地址暂存到寄存器t0，再通过指令csrw将该地址载入stvec。
    la t0, _traps 
    csrw stvec, t0
    
	# 阅读rv64文档得知开启时钟中断需要将sie寄存器的第4位置为1。
    # 先读取sie寄存器内容，将对应位置置为1后重新保存。
    csrr t0, sie
    ori t0,t0,32
    csrw sie, t0
    
	# 设置第一次时钟中断需要通过接口sbi_set_timer设置mtimecmp寄存器实现。
    # 通过ecall函数来调用接口sbi_set_timer。
    # 通过实验文档可知，sbi_set_timer的Function ID和Extension ID均为0。在调用ecall前，将a7、a6设为0。
    # a2则用于存放要设置的mtimecmp的值。通过rdtime读取当前时间，计算设置断点的时间并放在a0中。
    rdtime t0 # 读取当前时间。
    li t1, 10000000
    add a0, t0, t1 # 计算设置断点的时间，为当前时间+1s。
    li a1, 0
    li a2, 0
    li a3, 0
    li a4, 0
    li a5, 0
    li a6, 0 # 将a7、a6设为0。
    li a7, 0
    ecall # 调用ecall
    
	# 阅读rv64文档得知开启 S 态下的中断响应需要将sstatus的第1位置为1。设置方式与sie类似。
    # csrr t0, sstatus
    # ori t0,t0,2
    # csrw sstatus, t0
    
    call start_kernel # 最后调用start_kernel函数。

relocate:
    # set ra = ra + PA2VA_OFFSET
    # set sp = sp + PA2VA_OFFSET (If you have set the sp before)  
    # t2寄存器中载入了PA2VA_OFFSET的具体值
    li t2, 0xFFFFFFDF80000000 
    add ra, ra, t2
    add sp, sp, t2

    # set satp with early_pgtbl
    # 按照satp的格式要求，将其设置成(1<<63)|(&early_pgtbl>>12)
    # 注意，应该将early_pgtbl的实际物理地址加载到satp中，需要将early_pgtbl-PA2VA_OFFSET
    la t0, early_pgtbl
    sub t0, t0, t2
    srli t0, t0, 12
    li t1, 1
    slli t1, t1, 63
    or t0, t0, t1
    csrw satp, t0
    
    # flush tlb
    # 清除缓存
    sfence.vma zero, zero
		
    # flush icache
    fence.i
		
    ret

    .section .bss.stack
    .globl boot_stack
boot_stack:
    .space 4096

    .globl boot_stack_top
boot_stack_top: